package com.media;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 封装多媒体文件解析
 */
public class ConvertVideo {

    private List<String> pathRight = new ArrayList<String>();// 经处理后，得到的有效地址
    private List<String> path1 = new ArrayList<String>();// ffmpeg能处理的文件的地址集
    private List<String> path2 = new ArrayList<String>();// ffmpeg不能处理的文件的地址集
    public static int fileNum = 0;// 所有经过验证合法，并需要转换的视频数量

    private String mencoder_home = "E:/mediaCode/mplayer-svn-38151-x86_64/mencoder.exe";//mencoder.exe所放的路径
    private String mplay_home = "E:/mediaCode/mplayer-svn-38151-x86_64/mplayer.exe";//mplayer.exe所放的路径
    private String ffmpeg_home = "D:/Tools/ffmpeg/bin/ffmpeg.exe";//ffmpeg.exe所放的路径

    private String output = "E:/mediaCode/output/";//转换后的flv文件所放的文件夹位置
    private String temp = "E:/mediaCode/output/temp/";//存放rm,rmvb等无法使用ffmpeg直接转换为flv文件先转成的avi文件
    private String img = "E:/mediaCode/output/img/";//转换过程中产生的缩略图

    public static void main(String[] args) {
        Date start = new Date();
        ConvertVideo conver = new ConvertVideo();
        String fileFolder = "F:\\java\\work\\jingpinkecheng\\WebRoot\\upload\\input";
        List<String> listFile = conver.getFilesInPath(fileFolder);
        List<String> listVideo = conver.filter(listFile);
        int fileNumMax = listVideo.size(); // 所有视频数量
        conver.checkfile(listVideo);
        conver.process();

        if (fileNum == fileNumMax) {
            Date end = new Date();
            System.out.println("耗时：" + (end.getTime() - start.getTime()) / 1000
                    + "秒");
        }
    }

    /**
     * 过滤avi|wmv|rmvb|rm|asx|asf|mpg|3gp|mp4|mov|flv)$格式的视频
     */
    public List<String> filter(List<String> list) {
        List<String> fileList = new ArrayList<String>();
        for (String str : list) {
            if (str.matches(".+\\.(avi|wmv|rmvb|rm|asx|mkv|asf|mpg|3gp|mp4|mov|vob|flv)$")) {
                fileList.add(str);
            }
        }
        return fileList;
    }


    /**
     * 获取input文件夹下所有文件路径
     */
    public List<String> getFilesInPath(String path) {
        List<String> fileList = new ArrayList<String>();

        if (path == null || "".equals(path))
            return fileList;

        File file = new File(path);

        if (!file.exists()) {
            return fileList;
        }

        File[] childFiles = file.listFiles();

        try {
            for (int i = 0; i < childFiles.length; i++) {
                String filePath = childFiles[i].getAbsolutePath();
                if (childFiles[i].isDirectory()) {
                    fileList.addAll(getFilesInPath(filePath));
                } else {
                    fileList.add(filePath);
                }
            }
        } catch (Exception e) {
            return fileList;
        }

        return fileList;
    }


    /**
     * 获取input文件夹下指定文件路径
     *
     * @throws IOException
     */
    public String getSingleFileInPath(String path) throws IOException {

        if (path == null || "".equals(path)) {
            System.out.println("指定文件路径不存在！");
        }

        File file = new File(path);

        if (!file.exists()) {
            file.createNewFile();
        }
        return path;
    }


    /**
     * 避免文件重名，在文件尾部加唯一标识，主要是以获取当前时间，以字符串格式hhmmssSSSSSS
     */
    public String transform(Date date) {
        SimpleDateFormat sdFormat = new SimpleDateFormat("hhmmssSSSSSS ");
        String myTime = sdFormat.format(date);
        return myTime;
    }

    /**
     * 核心处理函数
     */
    public void process() {
        checkContentType(pathRight);
        pathRight.clear();
        if (path1.size() > 0) { // 地址集中的文件全部是ffmpeg所能解析的文件
            processFLV(path1); // 直接将文件转为flv文件
        }
        if (path2.size() > 0) {
            processAVI(path2); // 得到ffmpeg不能解析的文件所转换成avi文件的地址集
        }
    }

    /**
     * 将传过来的地址集分类，ffmpeg能解析的分到path1中，ffmpeg不能解析的分到path2中
     */
    public void checkContentType(List<String> list) {
        for (String str : list) {
            if (str.matches(".+\\.(avi|wmv|mkv|asx|asf|vob|mpg|mp4|mov|flv)$")) {
                path1.add(str);
            } else {
                path2.add(str);
            }
        }
    }

    /**
     * . 处理用户传递过来的地址集 有效的放到pathRight中 无效的放到pathWrong中
     */
    public void checkfile(List<String> path) {
        for (int i = 0; i < path.size(); i++) {
            File file = new File((String) path.get(i));
            if (file.isFile()) {
                pathRight.add(path.get(i));
            }
        }
    }

    /**
     * 对ffmpeg无法解析的文件格式(wmv9，rm，rmvb等), 可以先用别的工具（mencoder）转换为avi(ffmpeg能解析的)格式.
     */
    public void processAVI(List<String> list) {
        List<String> finish = new ArrayList<String>();
        for (String str : list) {
            List<String> commend = new java.util.ArrayList<String>();
            commend.add("\"" + mencoder_home + "\"");
            commend.add("\"" + str + "\"");
            commend.add("-oac");
            commend.add("mp3lame");
            commend.add("-lameopts");
            commend.add("preset=64");
            commend.add("-ovc");
            commend.add("xvid");
            commend.add("-xvidencopts");
            commend.add("bitrate=600");
            commend.add("-of");
            commend.add("avi");
            commend.add("-o");
            String file = str.substring(str.lastIndexOf("\\") + 1,
                    str.lastIndexOf("."));
            //file = file + transform(new Date());
            commend.add("\"" + temp + file + ".avi\""); // 最后输出出来的avi，尽量不要出现二义性，否则会覆盖掉原有的视频
            try {
                ProcessBuilder builder = new ProcessBuilder();
                builder.command(commend);
                Process p = builder.start();
                int exitValue = doWaitFor(p);
                if (exitValue != -1) {
                    processFLV1(temp + file + ".avi");
                }
            } catch (Exception e) {
                // e.printStackTrace();
                System.out.println("********avi转换出错********");
            }
            finish.add(str);
        }
        path2.removeAll(finish);
    }

    /**
     * ffmpeg能解析的格式：（asx，asf，mpg，wmv，3gp，mp4，mov，avi，flv等）
     */
    public boolean processFLV1(String str) {

        List<String> commend = new java.util.ArrayList<String>();
        commend.add("\"" + ffmpeg_home + "\"");
        commend.add("-i");
        commend.add("\"" + str + "\"");
        commend.add("-ab");
        commend.add("64");
        commend.add("-ac");
        commend.add("2");
        commend.add("-ar");
        commend.add("22050");
        commend.add("-b");
        commend.add("230");
        commend.add("-r");
        commend.add("29.97");
        commend.add("-y");
        String file = str.substring(str.lastIndexOf("\\") + 1,
                str.lastIndexOf("."));
        String fileName = output + file + ".flv";
        commend.add("\"" + fileName + "\"");
        try {
            ProcessBuilder builder = new ProcessBuilder();
            builder.command(commend);
            Process p = builder.start();
            int exitValue = doWaitFor(p);
            if (exitValue != -1) {
                fileNum++;
            }
            processImg(fileName);
        } catch (Exception e) {
            System.out.println("*********转换为FLV格式出错*********");
            // e.printStackTrace();
            return false;
        }
        deleteFile(str);
        return true;
    }

    public void processFLV(List<String> list) {
        List<String> finish = new ArrayList<String>();
        for (String str : list) {
            List<String> commend = new java.util.ArrayList<String>();
            commend.add("\"" + ffmpeg_home + "\"");
            commend.add("-i");
            commend.add("\"" + str + "\"");
            commend.add("-ab");
            commend.add("64");
            commend.add("-ac");
            commend.add("2");
            commend.add("-ar");
            commend.add("22050");
            commend.add("-b");
            commend.add("230");
            commend.add("-r");
            commend.add("29.97");
            commend.add("-y");
            String file = str.substring(str.lastIndexOf("\\") + 1,
                    str.lastIndexOf("."));
            String fileName = output + file /*+ transform(new Date())*/
                    + ".flv";
            commend.add("\"" + fileName + "\"");
            try {
                ProcessBuilder builder = new ProcessBuilder();
                builder.command(commend);
                Process p = builder.start();
                int exitValue = doWaitFor(p);
                if (exitValue != -1) {
                    fileNum++;
                    processImg(fileName);
                }
            } catch (Exception e) {
                // System.out.println("*********转换为FLV格式出错*********");
                e.printStackTrace();
            }
            finish.add(str);
        }
        path1.removeAll(finish);
    }

    /**
     * 生成图片 参数String newfilename, String newimg
     */
    public boolean processImg(String str) {
        List<String> commend = new java.util.ArrayList<String>();
        commend.add("\"" + ffmpeg_home + "\"");
        commend.add("-i");
        commend.add("\"" + str + "\"");
        commend.add("-y");
        commend.add("-f");
        commend.add("image2");
        commend.add("-ss");
        commend.add("5");
        commend.add("-t");
        commend.add("0.001");
        commend.add("-s");
        commend.add("320x240");
        commend.add("\"" + img
                + str.substring(10, str.lastIndexOf(".")).trim() + ".jpg\"");
        try {
            ProcessBuilder builder = new ProcessBuilder();
            builder.command(commend);
            builder.start();
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    public void deleteFile(String str) {
        File file = new File(str);
        file.delete();
    }

    /**
     * ffmpeg不能转换的格式要先转换成avi的格式，才转化为flv格式，doWaitFor方法保证，原视频完全转化为avi后，
     * 才转化成flv，避免avi,flv同时转换出错
     */
    public int doWaitFor(Process p) {
        InputStream in = null;
        InputStream err = null;
        int exitValue = -1; // returned to caller when p is finished
        try {
            System.out.println("comeing");
            in = p.getInputStream();
            err = p.getErrorStream();
            boolean finished = false; // Set to true when p is finished

            while (!finished) {
                try {
                    while (in.available() > 0) {
                        // Print the output of our system call
                        Character c = new Character((char) in.read());
                        System.out.print(c);
                    }
                    while (err.available() > 0) {
                        // Print the output of our system call
                        Character c = new Character((char) err.read());
                        System.out.print(c);
                    }

                    // Ask the process for its exitValue. If the process
                    // is not finished, an IllegalThreadStateException
                    // is thrown. If it is finished, we fall through and
                    // the variable finished is set to true.
                    exitValue = p.exitValue();
                    finished = true;

                } catch (IllegalThreadStateException e) {
                    // Process is not finished yet;
                    // Sleep a little to save on CPU cycles
                    Thread.currentThread().sleep(500);
                }
            }
        } catch (Exception e) {
            // unexpected exception! print it out for debugging...
            System.err.println("doWaitFor();: unexpected exception - "
                    + e.getMessage());
        } finally {
            try {
                if (in != null) {
                    in.close();
                }

            } catch (IOException e) {
                System.out.println(e.getMessage());
            }
            if (err != null) {
                try {
                    err.close();
                } catch (IOException e) {
                    System.out.println(e.getMessage());
                }
            }
        }
        // return completion status to caller
        return exitValue;
    }

}